#-*- coding:utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
import keras
import numpy as np
from keras import models
from keras import layers
from keras import optimizers
from keras import losses
from keras import metrics
import matplotlib.pyplot as plt
from keras.datasets import imdb
keras.__version__

# 书本的P43-P59




(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)


# 我们使用频率最高的10000个词语


# 0表示负面评论
# 1表示正面评论




# print"train_data[0]=",train_data[0]#这里是把一条评论数据里面的每个单词转换成编号（注意不是下标）
# print"train_labels[0]=",train_labels[0]



#因为我们限制使用频率最高的10000个单词，所以没有单词的下标会超过10000
max([max(sequence) for sequence in train_data])

# For kicks, here's how you can quickly decode one of these reviews back to English words:

# word_index is a dictionary mapping words to an integer index
word_index = imdb.get_word_index()
# We reverse it, mapping integer indices to words
#执行下面语句前，{key,value}={单词：数字编号}的形式
reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])
# 改成{数字编号：单词}的形式
# We decode the review; 
# note that our indices were offset by 3
# because 0, 1 and 2 are reserved indices for "padding", "start of sequence", and "unknown".
decoded_review = ' '.join([reverse_word_index.get(i - 3, '?') for i in train_data[0]])
# 这里的意思是，所有的编码都被加了3,以便于作者塞进去自定义的三个词语




decoded_review


# ## Preparing the data
# 我们不能直接把数据输入神经网络，我们需要把他们转化为张量。
# 有两种方式可以做到这一点，
# 一种是填充我们的列表，这样他们就会有相同的长度，
# 把它们变成形似“（样本，词编码）”的形式，然后作为神经网络的第一层，有能力来处理这样的整数张量；

# 一种是独热编码，例如`[3, 5]` 转化为10,000维度的向量，那么除了3和5，其他都是0，然后我们把它作为第一层，有能力处理浮点向量数据。

def vectorize_sequences(sequences, dimension=10000):#因为前面提到只使用频率最高的一万个词语。
    # Create an all-zero matrix of shape (len(sequences), dimension)
    results = np.zeros((len(sequences), dimension))
    for i, sequence in enumerate(sequences):
        results[i, sequence] = 1.  # set specific indices of results[i] to 1s
    return results

# Our vectorized training data
x_train = vectorize_sequences(train_data)
# Our vectorized test data
x_test = vectorize_sequences(test_data)


x_train[0]


#把标签向量化
y_train = np.asarray(train_labels).astype('float32')
y_test  = np.asarray(test_labels) .astype('float32')


#----------------------到底为止，数据准备完毕---------------------------

# 建立我们的神经网络
# 
# 我们的输入数据是简单的向量
# 我们的标签是标量（1s and 0s）

# 每个“Dense layer(16)”接收的参数是隐藏单元（就是放置激活函数的单元）

# 下面是张量的操作
# `output = relu(dot(W, input) + b)`
# 有16个隐藏单元意味着权重矩阵`W` 的维度是 `(input_dimension, 16)`
#注意，这里为什么有这个矩阵呢？
#因为是全连接层，所以每条数据都要输入 16个隐藏函数，所以10000条数据，就要16*10000个权重连接线。

# 这个关于W的点积，将会把输入数据投影到16维的表达空间。

# 对于 dense layers:
# 你需要确定
# 1.多少层
# 2.每层使用多少隐藏单元



# 现在，你要相信下面的选择：
# 两个中间层，每个16个隐藏单元

# 第三层输出关于“当前影评的情感”的标量预测，
# 中间层会使用relu作为他们的激活函数
#最后一层会使用sigmoid函数作为激活函数，这样就会输出概率（其实我个人理解，这里是把输出值限定在[0,1]之间）

# 神经网络的结构为：
# ![3-layer network](https://s3.amazonaws.com/book.keras.io/img/ch3/3_layer_network.png)

#下面的代码确定神经网络的具体结构
model = models.Sequential()
model.add(layers.Dense(16, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(16, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))


# 如果输出是一个概率，那么交叉熵（binary_crossentropy）是最好的选择
# 交叉熵是信息论中用来衡量不同概率分布之间的差距的。




model.compile(optimizer='rmsprop',
              loss='binary_crossentropy',
              metrics=['accuracy'])



# model.compile(optimizer=optimizers.RMSprop(lr=0.001),
#               loss=losses.binary_crossentropy,
#               metrics=[metrics.binary_accuracy])


# ## Validating our approach
# 
# In order to monitor during training the accuracy of the model on data that it has never seen before, we will create a "validation set" by 
# setting apart 10,000 samples from the original training data:




x_val = x_train[:10000]
partial_x_train = x_train[10000:]

y_val = y_train[:10000]
partial_y_train = y_train[10000:]

#------------下面开始训练(使用一部分数据)----------------
history = model.fit(partial_x_train,
                    partial_y_train,
                    epochs=20,#这里原来是20,为了节省时间改成1
                    batch_size=512,
                    validation_data=(x_val, y_val))


history_dict = history.history#history这个成员中包含了一切在训练的过程中发生的事情
print "history.keys()=",history_dict.keys()

# 上面这句代码中会输出4个key，他们对应训练和验证过程中监控的指标。





acc = history.history['acc']
val_acc = history.history['val_acc']#这个变量的意思是validation_accuracy
loss = history.history['loss']
val_loss = history.history['val_loss']#这个变量的意思是validation_loss

print"acc=",acc
print"loss=",loss
print"val_acc=",val_acc
print"val_loss=",val_loss
epochs = range(1, len(acc) + 1)

# "bo" is for "blue dot"
plt.plot(epochs, loss, 'bo', label='Training loss')
# b is for "solid blue line"
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()





plt.clf()   # clear figure
acc_values = history_dict['acc']
val_acc_values = history_dict['val_acc']

plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()

plt.show()


#图片上的点是训练损失和精度
# 而实线是验证集的损失和精度

# 由于初始化的不同，每次结果都会有些微的变化




model = models.Sequential()
model.add(layers.Dense(16, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(16, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

model.compile(optimizer='rmsprop',
              loss='binary_crossentropy',
              metrics=['accuracy'])

model.fit(x_train, y_train, epochs=4, batch_size=512)#使用所有训练集数据
results = model.evaluate(x_test, y_test)#进行预测

results


model.predict(x_test)


#在预测时你会发现，网络对于有些样本的预测结果十分自信(0.99 or more, or 0.01 or less)
# 但是对于其他样本却不是那么自信(0.6, 0.4). 


#进一步的实验,你可以做如下更改：
#使用更多的隐藏层
#使用更多的隐藏单元
#使用mse作为误差衡量的统计量，而不是`binary_crossentropy`
#使用tanh作为激活函数，而不是Relu


#结论：
# Here's what you should take away from this example:
# 
# 你需要做一些预处理工作，因为神经网络只能处理张量。

# Dense层配合relu激活函数，可以用来解决一系列问题，
# 这其中包括情感分类问题

# 对于二分类问题，你的神经网络应该这样结束：
# 一个Dense层，配合一个sigmoid激活函数
# 你的神经网络的输出应该是一个标量，在0和1之间，对概率编码.
# 对于一个标量sigmoid输出，使用的损失函数应该是binary_crossentropy.

